﻿using System;
using System.Collections.Generic;
using Tau.DataAccess;
using Tau.Model;
using System.Linq;
using System.Linq.Expressions;
using Tau.Service.Properties;
using Tau.ViewModels;
using Product = Tau.Model.Product;

namespace Tau.Service
{
    public partial class TauService
    {
        #region Admin

        public void AddProduct(Product product, List<int> categoryIds)
        {
            //map model to db object
            var adapter = new Adapters.Product();
            tblItem dbObject = adapter.GetDbObject(product);

            //perform the add
            _context.tblItems.AddObject(dbObject);
            _context.SaveChanges();

            if (categoryIds.Count > 0)
            {
                //add list of categories
                foreach (var categoryId in categoryIds)
                {
                    var newItemCategory = new tblItemCategory() { itemID = dbObject.itemID, categoryID = categoryId };
                    _context.tblItemCategories.AddObject(newItemCategory);
                }

                _context.SaveChanges();
            }
        }

        public void UpdateProduct(Product product, List<int> categoryIds)
        {
            //get existing page 
            tblItem dbItem = _context.tblItems.SingleOrDefault(i => i.itemID == product.ProductId);

            //map model to db object
            dbItem.hide = product.Hide;
            if (product.ImageId > 0)
            {
                dbItem.imageID = product.ImageId;
                dbItem.itemImg = string.Empty;
                dbItem.itemImgLg = string.Empty;
            }
            else
            {
                dbItem.itemImg = product.Image;
                dbItem.itemImgLg = product.LargeImage;
            }

            dbItem.itemName = product.ProductName;
            dbItem.itemNumber = product.ProductNumber;
            dbItem.itemShortDesc = product.ShortDescription;
            dbItem.price = product.Price;
            dbItem.shipping2 = product.Shipping2;
            dbItem.shippingCost = product.ShippingCost;
            dbItem.special = product.Special;
            dbItem.spotlight = product.Spotlight;
            dbItem.itemDesc = product.Description;

            //clear all existing categories
            var itemCategories = _context.tblItemCategories.Where(ic => ic.itemID == product.ProductId);
            foreach (var itemCategory in itemCategories)
            {
                _context.tblItemCategories.DeleteObject(itemCategory);
            }

            if (categoryIds.Count > 0)
            {
                //add list of categories
                foreach (var categoryId in categoryIds)
                {
                    var newItemCategory = new tblItemCategory() { itemID = product.ProductId, categoryID = categoryId };
                    _context.tblItemCategories.AddObject(newItemCategory);
                }
            }

            //get the existing page dbObject
            _context.SaveChanges();
        }

        public List<Product> GetAllProducts()
        {
            List<tblItem> dbItemList = _context.tblItems.ToList();
            return new Adapters.Product().GetModelList(dbItemList);
        }

        public ViewModels.Product GetProduct(int productId)
        {
            tblItem item = _context.tblItems.SingleOrDefault(p => p.itemID == productId);

            if (item != null)
            {
                ViewModels.Product productViewModel = new ViewModels.Product();
                productViewModel.ProductData = new Adapters.Product().GetModelObject(item);
                return productViewModel;
            }

            return null;
        }

        public AddEditProduct GetAddEditProductViewModel(int? productId)
        {
            AddEditProduct addEditProduct = new AddEditProduct();

            if (productId.HasValue)
            {
                tblItem item = _context.tblItems.SingleOrDefault(i => i.itemID == productId.Value);
                addEditProduct.EditingProduct = new Adapters.Product().GetModelObject(item);
                addEditProduct.SelectedCategories = GetCategoriesForProduct(productId.Value);
                addEditProduct.Mode = DisplayMode.Edit;

                if (addEditProduct.EditingProduct.ImageId > 0)
                {
                    Image image = 
                    addEditProduct.EditingImage = new Adapters.Image().GetModelObject(item.tblImage);
                }
            }
            else
            {
                addEditProduct.EditingProduct = new Product();
                addEditProduct.SelectedCategories = new List<Category>();
                addEditProduct.Mode = DisplayMode.Add;
            }
            
            addEditProduct.Categories = GetAllCategories();

            addEditProduct.Images = new ImageList { Images = GetAllImages(), Mode = DisplayMode.View };

            return addEditProduct;
        }

        public ProductList GetSortedProductList(string sortBy, bool sortDescending)
        {
            IQueryable<Product> productQuery = new Adapters.Product().GetModelList(_context.tblItems.ToList()).AsQueryable();

            var param = Expression.Parameter(typeof(Product), "product");
            var sortExpression = Expression.Lambda<Func<Product, object>>(Expression.Convert(Expression.Property(param, sortBy), typeof(object)), param);
            IQueryable<Product> sortedProducts = null;

            if (sortDescending)
            {
                sortedProducts = productQuery.OrderByDescending(sortExpression);
            }
            else
            {
                sortedProducts = productQuery.OrderBy(sortExpression);
            }

            return new ProductList()
            {
                Products = sortedProducts.ToList(),
                SortBy = sortBy,
                SortDescending = sortDescending
            };
        }

        public void DeleteProduct(int productId)
        {
            tblItem productToDelete = _context.tblItems.SingleOrDefault(p => p.itemID == productId);

            //delete from itemCategory
            var itemCategories = _context.tblItemCategories.Where(ic => ic.itemID == productId);
            foreach (var itemCategory in itemCategories)
            {
                _context.DeleteObject(itemCategory);
            }

            //delete the item
            _context.tblItems.DeleteObject(productToDelete);
            _context.SaveChanges();
        }

        #endregion

        #region Public

        public PublicProductIndexList GetPublicProductIndexList()
        {
            var returnProductIndexList = new PublicProductIndexList();

            returnProductIndexList.ProductSearch = new PublicProductSearch {Categories = new List<Category>()};

            List<Category> categories = GetPublicCategories();

            if (categories.Count > 0)
            {
                returnProductIndexList.PublicProductIndexItems = new List<PublicProductIndexItem>();

                foreach (var category in categories)
                {
                    returnProductIndexList.ProductSearch.Categories.Add(category);

                    var productIndexItem = new PublicProductIndexItem();

                    //Add category to product
                    productIndexItem.Category = category;

                    //Get abbreviated product list
                    productIndexItem.Products = GetSpotlightProductsInCategory(category.CategoryId);

                    if (productIndexItem.Products.Count > 0)
                    {
                        returnProductIndexList.PublicProductIndexItems.Add(productIndexItem);       
                    }
                }
            }

            return returnProductIndexList;
        }

        public List<Product> GetSpotlightProductsInCategory(int categoryId)
        {
            var publicProductList = new List<Product>();

            var dbItems = _context.tblItems         
                .Join(_context.tblItemCategories.Where(cat => cat.categoryID == categoryId),         
                      item => item.itemID,          
                      cat => cat.itemID,   
                      (item, cat) => new { tblItem = item })
                   .Select(result => result.tblItem)
                   .Take(Settings.Default.ProductsShownPerCategory)
                   .ToList();

            if (dbItems.Count > 0)
            {
                publicProductList = new Adapters.Product().GetModelList(dbItems);
                publicProductList = publicProductList.FindAll(product => (product.Spotlight || product.Special)  && !product.Hide);
            }

            return publicProductList;
        }

        /// <summary>
        /// Gets a list of matching products with the given category and search string
        /// </summary>
        /// <param name="productSearch"></param>
        /// <returns></returns>
        public PublicProductSearchResults GetFilteredProducts(PublicProductSearch productSearch)
        {
            PublicProductSearchResults searchResults = new PublicProductSearchResults();
            IQueryable<tblItem> dbItems = null;

            if (productSearch.PageNumber <= 1)
            {
                productSearch.PageNumber = 1;
            }

            searchResults.ProductSearch = new PublicProductSearch
                                              {
                                                  Categories = GetPublicCategories(), 
                                                  SelectedCategoryId = productSearch.SelectedCategoryId,
                                                  PageNumber = productSearch.PageNumber,
                                                  SearchString = productSearch.SearchString
                                              };

            if (productSearch.SelectedCategoryId > 0) //Get matching products in given category
            {
                dbItems = _context.tblItems
                    .Join(_context.tblItemCategories.Where(cat => cat.categoryID == productSearch.SelectedCategoryId),
                          item => item.itemID,
                          cat => cat.itemID,
                          (item, cat) => new { tblItem = item })
                       .Select(result => result.tblItem);
            }
            else //Get all matching products
            {
                dbItems = _context.tblItems;
            }

            if (!string.IsNullOrWhiteSpace(productSearch.SearchString))
            {
                dbItems = dbItems.Where(item => item.itemName.Contains(productSearch.SearchString));
            }
            
            if (dbItems.Count() > 0)
            {
                int totalProducts = dbItems.Count();

                //Get the correct page
                dbItems = dbItems
                    .OrderBy(item => item.itemName)
                    .Skip((productSearch.PageNumber - 1) * Settings.Default.ProductsPerPage)
                    .Take(Settings.Default.ProductsPerPage);
                
                searchResults.Products = new Adapters.Product().GetModelList(dbItems.ToList());

                searchResults.ProductsPerPage = Settings.Default.ProductsPerPage;
                searchResults.NumberOfPages = (int)Math.Ceiling((decimal)totalProducts / (decimal)searchResults.ProductsPerPage); 
            }

            return searchResults;
        }

        /// <summary>
        /// Gets a paged list of products within the given category
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public PublicProductSearchResults GetProductsInCategory(int id)
        {
            return GetFilteredProducts(new PublicProductSearch()
                                           {
                                               SelectedCategoryId = id
                                           });
        }

        #endregion
    }
}
